import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;

import aima.search.framework.Problem;
import aima.search.framework.Search;
import aima.search.framework.SearchAgent;
import aima.search.informed.SimulatedAnnealingSearch;
import aima.search.informed.HillClimbingSearch;

@SuppressWarnings("rawtypes")
public class IAPrac1BusquedaMain {
	
	private static int HC = 1;
	private static int SA = 2;
	private static Problem problem = null;
	private static Search search = null;
	private static SearchAgent sagent = null;
	
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		int N;
		int M;
		int alg;
		
		int numSteps = 10;
		int numIter = 1000;
		int k = 1;
		double lambda = 0.0001;
		
		long startTime, endTime;
		
		
		Scanner sc = new Scanner(System.in);
		System.out.println("Introduzca el numero de usuarios");
		N = sc.nextInt();
		
		System.out.println("Introduzca el numero de NO conductores");
		M = sc.nextInt();
		
		System.out.println("Escoja el tipo de algoritmo:"); 
		System.out.println("1. Hill Climbing");
		System.out.println("2. Simulated Annealing");
		alg = sc.nextInt();

		
        IAPrac1State initState = new IAPrac1State(N, M);
        
        startTime = System.currentTimeMillis();
	        initState.IAPracStateSolucionInicialB1();
	        if (alg == HC) IAPrac1BusquedaHillClimbing(initState);
	        else if (alg == SA) IAPrac1BusquedaSimulatedAnnealing(initState, numSteps, numIter, k, lambda);
        endTime = System.currentTimeMillis();
        
        System.out.println("\nExecution time: " + (endTime - startTime) + "ms");
        
        // Obtenemos la solucion final a partir del estado inicial
        List<String> lactions = sagent.getActions();
        int nactions = lactions.size();
        
        // Datos iniciales
        int dist1 = initState.getTotalHmsrec();
        int npas1 = initState.npas;
        
        // Obtenemos los resultados de la solucion inicial
        System.out.println("\nRegistro de usuarios:");
        initState.printRegistro();
        System.out.println("\nResultados solucion inicial:");
        System.out.println("- Distancia total conductores:    " + initState.getTotalHmsrec());
        System.out.println("- Numero de conductores:          " + initState.ncond);
        System.out.println("- Numero de pasajeros pendientes: " + initState.npas);
        System.out.println("\nRutas iniciales de los conductores:");
        initState.printConductores();
        
        // Aplicamos la secuencia de operadores para obtener la solucion final
        for (int i = 0; i < nactions; i++) {
        	String[] params = lactions.get(i).split("-");
        	if (params[0].compareTo(IAPrac1State.ELIMINAR_CONDUCTOR) == 0) {
        		// Formato: ELIMINAR_CONDUCTOR-con
        		initState.eliminarConductor(Integer.parseInt(params[1]));
        	}
        	else if (params[0].compareTo(IAPrac1State.ASIGNAR) == 0) {
        		// Formato: ASIGNAR-pas-con
        		initState.asignarPasajero(Integer.parseInt(params[1]), Integer.parseInt(params[2]));
        	}
        	else if (params[0].compareTo(IAPrac1State.ELIMINAR_PASAJERO) == 0) {
        		// Formato: ELIMINAR_PASAJERO-con-pas
        		initState.eliminarPasajero(Integer.parseInt(params[1]), Integer.parseInt(params[2]));
        	}
        	else if (params[0].compareTo(IAPrac1State.MOVER) == 0) {
        		// Formato: MOVER-con1-pas-con2
        		initState.moverPasajero(Integer.parseInt(params[1]), Integer.parseInt(params[2]), Integer.parseInt(params[3]));
        	}
        	else if (params[0].compareTo(IAPrac1State.INTERCAMBIAR_CONTIGUOS) == 0) {
        		// Formato: INTERCAMBIAR_CONTIGUOS-con-pos
        		initState.switchPasajeros(Integer.parseInt(params[1]), Integer.parseInt(params[2]));
        	}
        	else if (params[0].compareTo(IAPrac1State.INTERCAMBIAR) == 0) {
        		// Formato: INTERCAMBIAR-con-pos1-pos2
        		initState.switchPosicionesPasajeros(Integer.parseInt(params[1]), Integer.parseInt(params[2]), Integer.parseInt(params[3]));
        	}
        	else {
        		System.out.println("Codigo de operador no definido.");
        	}
        }
        
        // Obtenemos los resultados de la solucion final
        System.out.println("\nResultados solucion final:");
        System.out.println("- Distancia total conductores:    " + initState.getTotalHmsrec());
        System.out.println("- Numero de conductores:          " + initState.ncond);
        System.out.println("- Numero de pasajeros pendientes: " + initState.npas);
        System.out.println("\nRutas finales de los conductores:");
        initState.printConductores();
        
        // Formato: distancia_inicial, distancia_final, pasajeros_inicial, pasajeros_final, tiempo
        System.out.println(dist1);
        System.out.println(initState.getTotalHmsrec());
        System.out.println(npas1);
        System.out.println(initState.npas);
        System.out.println(endTime - startTime);
    }
	
	public static void IAPrac1BusquedaHillClimbing(IAPrac1State aState)
	{
		System.out.println("\nIAPrac1 Hill Climbing");
        try {
        	problem = new Problem(aState, new IAPrac1SuccessorFunction(), new IAPrac1GoalTest(), new IAPrac1HeuristicFunctionA());
        	search = new HillClimbingSearch();
        	sagent = new SearchAgent(problem, search); 
        	
        	System.out.println();
            printActions(sagent.getActions());
            printInstrumentation(sagent.getInstrumentation());
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
    }
	
	public static void IAPrac1BusquedaSimulatedAnnealing(IAPrac1State aState, int numSteps, int numIter, int k, double lambda)
	{
		System.out.println("\nIAPrac1 Simulated Annealing");
        try {
        	problem = new Problem(aState, new IAPrac1SuccessorFunctionSA(), new IAPrac1GoalTest(), new IAPrac1HeuristicFunctionA());
        	search = new SimulatedAnnealingSearch(numSteps, numIter, k, lambda );
        	sagent = new SearchAgent(problem, search); 
        	
        	System.out.println();
            printActions(sagent.getActions());
            printInstrumentation(sagent.getInstrumentation());
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
    }
	
	// Funciones auxiliares
	private static void printInstrumentation(Properties properties) {
        String key = null;
        String property = null;
		Iterator keys = properties.keySet().iterator();
        
		while (keys.hasNext()) {
            key = (String) keys.next();
            property = properties.getProperty(key);
            System.out.println(key + " : " + property);
        }
        
    }
    
	private static void printActions(List actions) {
        String action = null;
    	
    	for (int i = 0; i < actions.size(); i++) {
            action = (String) actions.get(i);
            System.out.println(action);
        }
    }
}